/*
 * 巴法科技:2022-11-22
 * 支持巴法app和小程序配网，长按按键可重新配网
 */
#include <Arduino.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <EEPROM.h>
#include <Ticker.h>
#include <ArduinoJson.h>
#include <httpUpdate.h>
#include <ESP32Ping.h>
#include "driver/touch_pad.h"

uint16_t touchThreshold = 2000;
touch_pad_t touchPin1 = TOUCH_PAD_NUM0;
touch_pad_t touchPin2 = TOUCH_PAD_NUM3;
WiFiClient client_bemfa_WiFiClient;
HTTPClient http_bemfa_HTTPClient;
// 巴法云服务器地址默认即可
#define server_ip "bemfa.com" // 巴法云服务器地址默认即可
#define server_port "8344"    // 服务器端口，tcp创客云端口8344

//*****可以修改的地方******//

String verSion = "3";           // 1是mqtt协议，3是tcp协议,5是mqtt V2 版本，7是 tcp V2 版本
String aptype = "006";          // 001插座类型，002灯类型，003风扇类型，004传感器，005空调，006开关，009窗帘
String adminID = "";            // 默认空即可。企业id,建议企业用户配置，用户会自动绑定到该企业下，获取id方法见接入文档5.17节
String device = "租房安防设备"; // 设备名称
String type = "2";              // 1表示是预警消息，2表示设备提醒消息
String getMinFreeHeap = "";

#define k0_Pin 22    // 卷帘门开关上拉引脚
#define k1_Pin 23    // 卷帘门开门引脚
#define buttonPin 0  // 定义按钮引脚
#define StatePin0 5  // 定义锁状态引脚
#define StatePin2 18 // 定义门状态引脚
#define StatePin1 19 // 定义锁状态引脚
#define StatePin3 2  // 定义指示灯态引脚

bool Stat = true;    // 锁状态
bool State = false;  // 锁状态标志
bool State0 = false; // 门状态
bool State1 = false; // 门状态标志
bool State2 = false; // 回家状态
bool State3 = false; // 回家状态标志
bool State4 = false; // 触摸把手状态
bool State5 = false; // 触摸反锁状态
bool State6 = false; // 反锁状态
bool State7 = false; // 夜间模式状态

hw_timer_t *tim1 = NULL; // 声明看门狗指针变量

std::vector<void (*)(String)> taskFunctions;
std::vector<String> TaskParameters;

// 校准时间参数
const char *ntpServer = "ntp2.aliyun.com";
const long gmtOffset_sec = 8 * 3600;
const int daylightOffset_sec = 0;

// 最大字节数
#define MAX_PACKETSIZE 512
// 设置心跳值30s
#define KEEPALIVEATIME 100

// tcp客户端相关初始化，默认即可
String TcpClient_Buff = "";
int failCount = 0;            // 定义失败连接次数
bool preTCPConnected = false; // 初始化和服务器建立连接状态
unsigned int TcpClient_BuffIndex = 0;
unsigned long TcpClient_preTick = 0;
unsigned long preHeartTick = 0;    // 心跳
unsigned long preTCPStartTick = 0; // 连接

/**
 * 结构体，用于存储配网信息
 */
struct config_type
{
  uint8_t reboot;
  uint8_t magic;
  char stassid[32];
  char stapsw[64];
  char cuid[40];
  char ctopic[32];
  char ip_1[16];
  char ip_2[16];
};
config_type config;
uint8_t *p = (uint8_t *)(&config);

// 相关函数初始化
// 连接WIFI
void doWiFiTick();
void initWiFi();
void waitKey();
void doSmartconfig();
void saveConfig();
void loadConfig();
void restoreFactory();
void StatedoHttpStick();

// TCP初始化连接
void doTCPClientTick();
void startTCPClient();
void sendtoTCPServer(String p);

int httpCode = 0;
char config_flag = 0;
String UID = "";
String TOPIC = "";
#define HOST_NAME "bemfa"

// 升级超时中断函数
void tim1Interrupt()
{
  sendtoTCPServer("cmd=2&uid=" + UID + "&topic=" + TOPIC + "&msg=升级超时系统重启\r\n"); // 推送消息
  // Serial.println("升级超时系统重启");
  delay(1000);
  esp_restart();
}

// 当升级中，打印日志
void update_progress(int cur, int total)
{
  timerWrite(tim1, 0); // 喂狗操作，计时器清零
  static bool update = false;
  int bfb = map(cur, 0, total, 0, 100);
  if (bfb % 5 == 0)
  {
    if (!update)
    {
      if (bfb == 100)
      {
        delay(1000);
      }
      client_bemfa_WiFiClient.print("cmd=2&uid=" + UID + "&topic=" + TOPIC + "&msg=" + TOPIC + "#" + String(bfb) + " % \r\n");
      update = true;
      Serial.printf("CALLBACK:  HTTP update process at %d of %d bytes... %d %\n", cur, total, bfb);
    }
  }
  else
  {
    update = false;
  }
}

/**
 * 固件升级函数
 * 在需要升级的地方，加上这个函数即可，例如setup中加的updateBin();
 * 原理：通过http请求获取远程固件，实现升级
 */

void updateBin()
{
  String upUrl = "http://bin.bemfa.com/b/3BcNDg5MmJkN2ZlMDA1ZWNiYmNjZjM1OTI5MTU3ZWM3ZTc=" + TOPIC + ".bin";

  // 初始化看门狗定时器
  tim1 = timerBegin(1, 80, true);
  timerAttachInterrupt(tim1, tim1Interrupt, true);
  timerAlarmWrite(tim1, 30000 * 1000, true); // 30s触发
  timerAlarmEnable(tim1);
  WiFiClient UpdateClient;
  httpUpdate.onProgress(update_progress); // 当升级中

  t_httpUpdate_return ret = httpUpdate.update(UpdateClient, upUrl);
  switch (ret)
  {
  case HTTP_UPDATE_FAILED: // 当升级失败
    break;
  case HTTP_UPDATE_NO_UPDATES: // 当无升级
    break;
  case HTTP_UPDATE_OK: // 当升级成功
    break;
  }
}

Ticker delayTimer;
void delayRestart(float t)
{
  delayTimer.attach(t, []()
                    { ESP.restart(); });
}

void getUid(String mac, bool reConfig)
{
  if (strcmp(config.cuid, "4892bd7fe005ecbbccf35929157ec7e7") != 0 || reConfig)
  {
    http_bemfa_HTTPClient.begin(client_bemfa_WiFiClient, "http://api.bemfa.com/api/device/v1/airkiss/?topic=" + mac + "&version=" + verSion + "&ad=" + adminID);
    httpCode = http_bemfa_HTTPClient.GET();
    if (httpCode > 0)
    {
      String payload = http_bemfa_HTTPClient.getString();
      // json数据解析
      StaticJsonDocument<200> doc;
      DeserializationError error = deserializeJson(doc, payload);
      if (error)
      {
        // Serial.print(F("de//SerializeJson() failed: "));
        // Serial.println(error.c_str());
      }
      String code = doc["code"];
      if (code == "5723200")
      {

        String docUID = doc["uid"];
        strcpy(config.cuid, docUID.c_str());
        strcpy(config.ctopic, mac.c_str());
        saveConfig();
      }
      else
      {
        // Serial.println(" config ERROR.........");
      }
      http_bemfa_HTTPClient.end();
    }
  }
  if (strcmp(config.ctopic, "") == 0)
  {
    TOPIC = mac;
  }
  else
  {
    TOPIC = config.ctopic;
  }
  UID = config.cuid;
  // Serial.printf("密钥:%s \n", config.cuid);
  // Serial.printf("主题:%s \n", config.ctopic);
  // Serial.printf("网络:%s \n", config.stassid);
  // Serial.printf("密码:%s \n", config.stapsw);
}

/**
 * 加载存储的信息，并检查是否进行了反复5次重启恢复出厂信息
 */
void loadConfig()
{
  if (EEPROM.begin(256))
  {
    for (int i = 0; i < sizeof(config); i++)
    {
      *(p + i) = EEPROM.read(i);
    }
    Serial.println("EEPROM初始成功");

  }
  else
  {
    Serial.println("EEPROM初始失败");
  }
  Serial.printf("密钥:%s \n", config.cuid);
  Serial.printf("主题:%s \n", config.ctopic);
  Serial.printf("网络:%s \n", config.stassid);
  Serial.printf("密码:%s \n", config.stapsw);
  Serial.printf("IP地址1:%s \n", config.ip_1);
  Serial.printf("IP地址2:%s \n", config.ip_2);
  config.reboot = config.reboot + 1;
  if (config.reboot >= 10)
  {
    restoreFactory();
  }
  if (config.magic != 0xAA)
  {
    config_flag = 1;
  }

  EEPROM.begin(256);
  for (int i = 0; i < sizeof(config); i++) // 写数据
  {
    EEPROM.write(i, *(p + i)); // 写数据
  }
  EEPROM.commit(); // 保存更改的数据

  delay(500);

  EEPROM.begin(256);
  config.reboot = 0;
  for (int i = 0; i < sizeof(config); i++)
  {
    EEPROM.write(i, *(p + i));
  }
  EEPROM.commit();
}

/**
 * 恢复出厂设置，清除存储的wifi信息
 */
void restoreFactory()
{
  // Serial.println("Restore Factory....... ");
  config.magic = 0x00;
  strcpy(config.stassid, "");
  strcpy(config.stapsw, "");
  strcpy(config.cuid, "");
  strcpy(config.ctopic, "");
  strcpy(config.ip_1, "");
  strcpy(config.ip_2, "");
  saveConfig();
  delayRestart(1);
  while (1)
  {
    delay(100);
  }
}
/**
 * 检查是否需要airkiss配网
 */
void waitKey()
{
  if (config_flag == 1)
  {
    doSmartconfig();
  }
}
/**
 * airkiss配网
 */
void doSmartconfig()
{
  WiFi.disconnect();
  WiFi.mode(WIFI_STA);
  WiFi.stopSmartConfig();
  WiFi.beginSmartConfig();
  while (!WiFi.smartConfigDone())
  {
    delay(100);
  }
  strcpy(config.stassid, WiFi.SSID().c_str());
  strcpy(config.stapsw, WiFi.psk().c_str());
  config.magic = 0xAA;
  saveConfig();
  WiFi.disconnect();                         // 断开连接
  WiFi.mode(WIFI_STA);                       // STA模式
  WiFi.begin(config.stassid, config.stapsw); // 连接路由器
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(1000);
  }
  delay(1000);
}

/**
 * 存储配网信息
 */
void saveConfig()
{
  EEPROM.begin(256);
  uint8_t *p = (uint8_t *)(&config);
  for (int i = 0; i < sizeof(config); i++)
  {
    EEPROM.write(i, *(p + i));
  }
  EEPROM.commit();
}

/**
 * 初始化wifi信息，并连接路由器网络
 */
void initWiFi()
{
  if (WiFi.status() != WL_CONNECTED)
  {
    WiFi.disconnect();                         // 断开连接
    WiFi.mode(WIFI_STA);                       // STA模式
    WiFi.begin(config.stassid, config.stapsw); // 连接路由器
  }
}

/*
  WiFiTick
  检查是否需要初始化WiFi
  检查WiFi是否连接上，若连接成功启动TCP Client
  控制指示灯
*/
void doWiFiTick()
{
  static bool taskStarted = false;
  static uint32_t lastWiFiCheckTick = 0;

  if (WiFi.status() != WL_CONNECTED) // 检查是否连接成功
  {
    if (millis() - lastWiFiCheckTick > 10000) // 未连接重连
    {
      initWiFi();
      taskStarted = false;
      lastWiFiCheckTick = millis();
    }
  }
  else if (taskStarted == false)
  {
    taskStarted = true;
    startTCPClient();
  }
}
/*
 *发送数据到TCP服务器
 */
void sendtoTCPServer(String p)
{
  if (client_bemfa_WiFiClient.connected() && !client_bemfa_WiFiClient.print(p))
  {
    client_bemfa_WiFiClient.stop();
  }
  preTCPConnected = false;
}
/*
 *初始化和服务器建立连接
 */
void startTCPClient()
{
  static int Count = 0;

  if (client_bemfa_WiFiClient.connect(server_ip, atoi(server_port)))
  {
    String tcpTemp = "";                                       // 初始化字符串
    tcpTemp = "cmd=1&uid=" + UID + "&topic=" + TOPIC + "\r\n"; // 构建订阅指令
    sendtoTCPServer(tcpTemp);                                  // 发送订阅指令
    tcpTemp = "";                                              // 清空
    /*
     //如果需要订阅多个主题，可发送  cmd=1&uid=xxxxxxxxxxxxxxxxxxxxxxx&topic=xxx1,xxx2,xxx3,xxx4\r\n
    教程：https://bbs.bemfa.com/64
     */
    client_bemfa_WiFiClient.setNoDelay(true);
  }
  else
  {
    Count++;
    if (failCount < Count) // 如果失败连接10次，重启系统
    {
      WiFi.disconnect(); // 断开连接
      failCount = 0;
    }
    client_bemfa_WiFiClient.stop();
  }
  preTCPStartTick = millis();
}

String convert(bool S)
{
  if (S)
  {
    return "打开";
  }
  else
  {
    return "关闭";
  }
}

String OnOff(bool S)
{
  if (S)
  {
    return "on";
  }
  else
  {
    return "off";
  }
}

void doHttpStick(void (*function)(String), String param) // 给列表添加任务函数
{
  taskFunctions.push_back(function);
  TaskParameters.push_back(param);
}

void taskdoHttpStick() // 执行列表中的任务 并删除任务
{
  size_t i = 0;
  while (taskFunctions.size())
  {
    taskFunctions[i](TaskParameters[i]);
    taskFunctions.erase(taskFunctions.begin() + i);
    TaskParameters.erase(TaskParameters.begin() + i);
  }
}

void StatedoHttpStick()
{
  sendtoTCPServer("cmd=2&uid=" + UID + "&topic=" + TOPIC + "/up&msg=" + OnOff(Stat) + "#" + OnOff(State0) + "#" + OnOff(State2) + "#" + OnOff(State4) + "#" + OnOff(State6) + "#" + OnOff(State7) + "#" + String(analogRead(33)) + "" + getMinFreeHeap + " \r\n");
}

void taskdoHttpStick(String param) // 任务函数
{
  WiFiClient WiFi;
  HTTPClient http;
  String ApiUrl = "http://api.bemfa.com/api/wechat/v1/"; // 默认 api 网址
  String postData = "uid=" + UID + "&type=" + type + "&device=" + device + "&msg=" + param;
  http.begin(WiFi, ApiUrl);                                            // Specify request destination
  http.addHeader("Content-Type", "application/x-www-form-urlencoded"); // Specify content-type header
  http.POST(postData);                                                 // Send the request
  http.end();
  WiFi.stop();
  ApiUrl = "";
  postData = "";
}

void doTCPClientTick()
{
  // 检查是否断开，断开后重连
  if (WiFi.status() != WL_CONNECTED)
  {
    preTCPStartTick = millis();
    return;
  }
  if (!preTCPConnected)
  {
    if (millis() - preTCPStartTick > 10000) // 重新连接
    {
      startTCPClient();
    }
  }
  if (client_bemfa_WiFiClient.connected())
  {
    if (client_bemfa_WiFiClient.available())
    { // 收数据
      char c = client_bemfa_WiFiClient.read();
      TcpClient_Buff += c;
      TcpClient_BuffIndex++;
      TcpClient_preTick = millis();

      if (TcpClient_BuffIndex >= MAX_PACKETSIZE - 1)
      {
        TcpClient_BuffIndex = MAX_PACKETSIZE - 2;
        TcpClient_preTick = TcpClient_preTick - 200;
      }
    }
  }
  if ((TcpClient_Buff.length() >= 20) && (millis() - TcpClient_preTick >= 200))
  {
    preTCPConnected = true;
    preTCPStartTick = millis();
    client_bemfa_WiFiClient.flush();
    if (TcpClient_Buff.indexOf("update") > 0)
    {
      updateBin();
    }
    else if (TcpClient_Buff.indexOf("Reboot") > 0)
    {
      delayRestart(1);
    }
    else if (!State0 && TcpClient_Buff.indexOf("on") > 0) // 如果是消息==打开
    {
      Stat = true;
    }
    else if (!State0 && TcpClient_Buff.indexOf("off") > 0) // 如果是消息==关闭
    {
      Stat = false;
    }
    else if (TcpClient_Buff.indexOf("ping") > 0)
    {
      int a = TcpClient_Buff.indexOf("1ping");
      int b = TcpClient_Buff.indexOf("2ping");
      if (a > 0)
      {
        int c = TcpClient_Buff.indexOf("\n", a);
        String VaLue = TcpClient_Buff.substring(a + 5, c);
        strcpy(config.ip_1, VaLue.c_str());
        saveConfig();
      }
      if (b > 0)
      {
        int c = TcpClient_Buff.indexOf("\n", b);
        String VaLue = TcpClient_Buff.substring(b + 5, c);
        strcpy(config.ip_2, VaLue.c_str());
        saveConfig();
      }
      sendtoTCPServer("cmd=2&uid=" + UID + "&topic=" + TOPIC + "/up&msg=1ping" + config.ip_1 + " 2ping" + config.ip_2 + "\r\n");
    }
    TcpClient_Buff = "";
    TcpClient_BuffIndex = 0;
  }
}

void task(void *parameter)
{
  static uint32_t lj = 30000;
  static uint32_t sslj = 0;
  static uint32_t lllj = 0;
  static bool ss = false;
  static bool ll = false;
  while (true)
  {
    vTaskDelay(pdMS_TO_TICKS(2000));
    State2 = false;
    ss = false;
    ll = false;
    while (WiFi.status() == WL_CONNECTED && config.ip_1 != "" && config.ip_2 != "")
    {
      taskdoHttpStick();

      if (Ping.ping(config.ip_1, 1) || Ping.ping(config.ip_2, 1))
      {
        State2 = true;
      }
      else
      {
        State2 = false;
      }

      vTaskDelay(pdMS_TO_TICKS(1000));
      // 保持心跳
      sendtoTCPServer("cmd=0&msg=keep\r\n");
      getMinFreeHeap = "#" + String(ESP.getMinFreeHeap()) + "";
      struct tm timeinfo;
      if (getLocalTime(&timeinfo))
      {
        if (timeinfo.tm_hour >= 19 || timeinfo.tm_hour <= 6)
        {
          State7 = true;
        }
        else
        {
          State7 = false;
        }
      }
    }
  }
  vTaskDelete(NULL);
}

void taskping(void)
{
  xTaskCreatePinnedToCore(
      task,   /* Task function. */
      "task", /* String with name of task. */
      10000,  /* Stack size in bytes. */
      NULL,   /* Parameter passed as input of the task */
      1,      /* Priority of the task. */
      NULL,
      0); /* Task handle. */
}

void Onoff(void *parameter)
{
  static uint32_t yanshi;
  static uint32_t huijiayanshi;
  while (true)
  {
    if (Stat != State)
    {
      if (Stat)
      {
        digitalWrite(k1_Pin, LOW);
        digitalWrite(k0_Pin, HIGH);
        vTaskDelay(pdMS_TO_TICKS(2000));
        digitalWrite(k0_Pin, LOW);
        doHttpStick(taskdoHttpStick, "门已下锁");
      }
      else
      {
        digitalWrite(k0_Pin, LOW);
        digitalWrite(k1_Pin, HIGH);
        vTaskDelay(pdMS_TO_TICKS(2000));
        digitalWrite(k1_Pin, LOW);
        doHttpStick(taskdoHttpStick, "门已上锁");
      }
      State = Stat;
      StatedoHttpStick();
    }

    if (Stat != State0) // 锁和门不同步
    {
      if (!State0 && ((State7 && analogRead(33) == 4095) || !State2)) // 如果门是关闭状态，夜深或者无人自动反锁
      {
        if (millis() - yanshi >= 10000)
        {
          State6 = true; // 反锁
          Stat = false;  // 上锁
        }
      }
      else
      {
        yanshi = millis();
      }
      if (State0) // 如果门打开同步锁状态
      {
        Stat = true;
        State6 = false;
      }
    }
    else
    {
      yanshi = millis();
    }

    if (State0 != State1) //  更新开门关门状态
    {
      // doHttpStick(taskdoHttpStick, "门已经" + convert(State0));
      // if (!State0 && !State2)
      // {
      //   Stat = false; // 上锁
      // }
      State1 = State0;
      StatedoHttpStick();
    }

    if (!State0 && State2 != State3) //  更新家里有没有主人状态
    {
      StatedoHttpStick();
      if (!State2)
      {
        // doHttpStick(taskdoHttpStick, "离家");
        huijiayanshi = millis(); // 离家后开始计时
      }
      else if (millis() - huijiayanshi >= 60000) // 离家超过一分钟后回家就自动下锁
      {
        // doHttpStick(taskdoHttpStick, "回家");
        Stat = true; // 下锁
      }
      State3 = State2;
    }

    if (!State0 && State4 != State5) // 更新触摸开关状态
    {
      if (State4) // 触摸把手开关
      {
        State5 = State4;
        if (!State6 && State2)
        {
          Stat = true; // 下锁
        }
      }
      else if (State5) // 触摸反锁开关
      {
        State4 = State5;
        if (!State)
        {
          State6 = false; // 反锁下锁
          Stat = true;    // 下锁
          // doHttpStick(taskdoHttpStick, "反锁下锁");
        }
        else
        {
          State6 = true; // 反锁上锁
          Stat = false;  // 上锁
          // doHttpStick(taskdoHttpStick, "反锁上锁");
        }
      }
      StatedoHttpStick();
    }
    vTaskDelay(pdMS_TO_TICKS(100));
  }
  vTaskDelete(NULL);
}
void taskOnoff(void)
{
  xTaskCreate(
      Onoff,            /* Task function. */
      "Onoff",          /* String with name of task. */
      10000,            /* Stack size in bytes. */
      NULL,             /* Parameter passed as input of the task */
      tskIDLE_PRIORITY, /* Priority of the task. */
      NULL);            /* Task handle. */
}

void AutoState()
{
  if (millis() - preHeartTick >= KEEPALIVEATIME)
  {
    static uint32_t touch1 = 200;
    static uint32_t touch2 = 150;
    static uint32_t touchRead;
    static uint32_t touchRead1;
    static uint32_t touchRead2;

    uint16_t touchReadValue1;
    touch_pad_read(touchPin1, &touchReadValue1);
    uint16_t touchReadValue2;
    touch_pad_read(touchPin2, &touchReadValue2);
    State0 = digitalRead(StatePin0);
    // sendtoTCPServer("cmd=2&uid=" + UID + "&topic=" + TOPIC + "/up&msg=把手：" + touchReadValue1 + "#" + touch1 + "，反锁：" + touchReadValue2 + "#" + touch2 + "\r\n"); // 推送消息
    if (touchReadValue1 < touch1)
    {
      State4 = true;
      digitalWrite(StatePin3, HIGH);
    }
    else if (touchReadValue2 < touch2)
    {
      State5 = true;
      digitalWrite(StatePin3, HIGH);
    }
    else if (State4 || State5)
    {
      State4 = false;
      State5 = false;
      digitalWrite(StatePin3, LOW);
    }
    else
    {
      touchRead++;
      touchRead1 += touchReadValue1;
      touchRead2 += touchReadValue2;
      if (touchRead > 100)
      {
        touch1 = touchRead1 / 100 * 0.8;
        touch2 = touchRead2 / 100 * 0.8;
        touchRead = touchRead1 = touchRead2 = 0;
      }
    }
    preHeartTick = millis();
  }
}

// 初始化，相当于main 函数
void setup()
{
  Serial.begin(115200);

  loadConfig();
  waitKey();
  initWiFi();

  TOPIC = WiFi.macAddress().substring(8); // 取mac地址做主题用
  TOPIC.replace(":", "");                 // 去掉:号
  TOPIC = TOPIC + aptype;
  getUid(TOPIC, false);

  pinMode(k0_Pin, OUTPUT);
  pinMode(k1_Pin, OUTPUT);

  touch_pad_init();
  touch_pad_config(touchPin1, touchThreshold);
  touch_pad_config(touchPin2, touchThreshold);

  pinMode(StatePin0, INPUT_PULLUP);
  pinMode(StatePin3, OUTPUT);
  pinMode(StatePin2, OUTPUT);
  pinMode(StatePin1, OUTPUT);

  digitalWrite(StatePin2, LOW);
  digitalWrite(StatePin1, HIGH);

  pinMode(25, OUTPUT);
  digitalWrite(25, HIGH);
  pinMode(32, OUTPUT);
  digitalWrite(32, LOW);
  pinMode(33, INPUT);

  pinMode(buttonPin, INPUT_PULLUP);
  digitalWrite(k0_Pin, LOW);
  digitalWrite(k1_Pin, LOW);

  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); // 时间初始化

  taskping();
  taskOnoff();
}

void loop()
{
  delay(1);
  AutoState();
  doWiFiTick();
  doTCPClientTick(); // tcp消息接收
}